package pers.supeijin.devtools.converter.wx2vue.converter;

import com.intellij.lang.javascript.psi.JSArgumentList;
import com.intellij.lang.javascript.psi.JSCallExpression;
import com.intellij.lang.javascript.psi.JSExpression;
import com.intellij.lang.javascript.psi.JSExpressionStatement;
import com.intellij.lang.javascript.psi.JSFunction;
import com.intellij.lang.javascript.psi.JSObjectLiteralExpression;
import com.intellij.lang.javascript.psi.JSProperty;
import com.intellij.lang.javascript.psi.impl.JSChangeUtil;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiRecursiveElementVisitor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.xml.XmlTag;

import org.jetbrains.annotations.NotNull;

import java.util.HashMap;
import java.util.Objects;

import pers.supeijin.devtools.converter.base.BaseConverter;
import pers.supeijin.devtools.converter.utils.CommonUtils;
import pers.supeijin.devtools.converter.utils.StringUtils;
import pers.supeijin.devtools.converter.wx2vue.converter.script.EventConvertUtils;
import pers.supeijin.devtools.converter.wx2vue.converter.script.LifeCycleConvertUtils;
import pers.supeijin.devtools.converter.wx2vue.converter.script.LocalJSReferenceConvertUtils;
import pers.supeijin.devtools.utils.GeneralContext;

//脚本转换器
public class ScriptConverter extends BaseConverter {
	private static final String FUNCTION_TAG = ":\\s*function";
	private static final String PROPERTIES_BOSERVER_TAG = "observer";//小程序的单个属性监听标志符号
	private static final String BOSERVERS_TAG = "observers";//小程序的监听标志符号
	private static final String WATCH_TAG = "watch";//Vue的监听标志符
	private static final String METHODS_TAG = "methods";//methods标志符
	private static final String SCRIPT_TAG = "\t\n<script>\t\n %s\t\n</script>";

	public ScriptConverter(GeneralContext generalContext, VirtualFile jsFile) {
		super(generalContext,jsFile);
	}

	public XmlTag getScriptTag() {
		Pair<Boolean, JSObjectLiteralExpression> info = getJSFileInfo(mPsiFile);
		String srciptText;
		if (info.first && info.second != null) {
			HashMap<String, String> observerMap = convertPropties(info.second);//转换组件属性
			convertObservers(info.second, observerMap);//转换监听函数
			convertData(info.second);//转换datas 属性
			convertCallExpression(info.second);//转换 this.setData()调用
			EventConvertUtils.converttriggerEvent(info.second);//转换this.triggerEvent('', {}) 事件发送调用
			LifeCycleConvertUtils.convertLifeCycleProperties(info.second);//转换生命周期函数
			LocalJSReferenceConvertUtils.convertFieldsCallExpression(info.second);//转换本地字段（数据调用）  this.data.userName 或者 this.properties.userName
			srciptText = "export default " + info.second.getText();
			srciptText = StringUtils.replaceAll(srciptText, FUNCTION_TAG, "");//将ES5标准属性函数替换为ES6函数
			//srciptText = StringUtils.replaceAll(srciptText, this.properties., "this.");//替换组件属性调用方式
		} else {
			srciptText = "export default {\t\n}\t\n";
		}
		return getXmlTag(String.format(SCRIPT_TAG, srciptText));
	}

	private void convertData(JSObjectLiteralExpression jsObjectLiteralExpression) {
		if (jsObjectLiteralExpression == null) {
			return;
		}
		JSProperty dataProperty = jsObjectLiteralExpression.findProperty("data");
		if (dataProperty != null) {
			String functionText = "data(){\t\n  return " + dataProperty.getValue().getText() + "\t\n}";
			JSFunction jsFunction = JSChangeUtil.createClassMemberPsiFromTextWithContext(functionText, jsObjectLiteralExpression, JSFunction.class);
			dataProperty.replace(jsFunction);
		}
	}

	//转换属性，同时返回属性的监听函数
	private HashMap<String, String> convertPropties(JSObjectLiteralExpression jsObjectLiteralExpression) {
		HashMap<String, String> propertyMap = new HashMap<>();
		if (jsObjectLiteralExpression == null) {
			return propertyMap;
		}
		JSProperty propsProperty = jsObjectLiteralExpression.findProperty("properties");
		if (propsProperty != null) {
			JSObjectLiteralExpression literalExpression = PsiTreeUtil.getChildOfType(propsProperty, JSObjectLiteralExpression.class);
			if (literalExpression != null) {
				JSProperty[] properties = literalExpression.getProperties();
				for (JSProperty prop : properties) {
					JSObjectLiteralExpression detailJSObjectLiteralExpression = PsiTreeUtil.getChildOfType(prop, JSObjectLiteralExpression.class);
					JSProperty[] detailProperties = detailJSObjectLiteralExpression.getProperties();
					for (JSProperty property : detailProperties) {
						if (Objects.equals(property.getName(), "value")) {
							property.setName("default");
						} else if (Objects.equals(property.getName(), PROPERTIES_BOSERVER_TAG)) {
							propertyMap.put(prop.getName(), property.getText());
							property.delete();
						}
					}
				}
			}
			propsProperty.setName("props");
		}
		return propertyMap;
	}

	//转换监听函数
	private void convertObservers(JSObjectLiteralExpression jsObjectLiteralExpression, HashMap<String, String> observerMap) {
		JSProperty property = jsObjectLiteralExpression.findProperty(BOSERVERS_TAG);
		boolean isNew = false;
		if (property == null) {
			isNew = true;
			String propertyText = BOSERVERS_TAG + ":{\t\n  }";
			property = (JSProperty) JSChangeUtil.createObjectLiteralPropertyFromText(propertyText, jsObjectLiteralExpression);
		}
		property.setName(WATCH_TAG);
		JSObjectLiteralExpression observersJSObjectLiteralExpression = PsiTreeUtil.getChildOfType(property, JSObjectLiteralExpression.class);
		observerMap.forEach((propName, observerPropertyText) -> {
			String newPropertyText = observerPropertyText.replace(PROPERTIES_BOSERVER_TAG, propName);
			PsiElement literalPropertyFromText = JSChangeUtil.createObjectLiteralPropertyFromText(newPropertyText, observersJSObjectLiteralExpression);
			PsiElement child = observersJSObjectLiteralExpression.getFirstChild();
			final PsiElement newLine = JSChangeUtil.createNewLine(observersJSObjectLiteralExpression);
			PsiElement parent = PsiTreeUtil.getStubOrPsiParent(child);
			//String literalPropertyFromTextText = literalPropertyFromText.getText();
			parent.addAfter(literalPropertyFromText, child);
			parent.addAfter(newLine, child);
		});
		if (isNew) {
			PsiElement insertPositon = jsObjectLiteralExpression.findProperty(METHODS_TAG);
			if (insertPositon == null) {
				insertPositon = jsObjectLiteralExpression.getLastChild();
			}
			final PsiElement newLine = JSChangeUtil.createNewLine(jsObjectLiteralExpression);
			PsiElement parent = PsiTreeUtil.getStubOrPsiParent(insertPositon);
			parent.addBefore(property, insertPositon);
			parent.addBefore(newLine, insertPositon);
		}
	}


	//替换this.setData（）this.setData部分代码
	private void convertCallExpression(JSObjectLiteralExpression jsObjectLiteralExpression) {
		JSExpressionStatement[] expressionStatements = CommonUtils.getJSExpressionStatements(jsObjectLiteralExpression, element -> element.getText().startsWith("this.setData"));
		for (PsiElement jsExpressionStatement : expressionStatements) {
			JSCallExpression jsCallExpression = PsiTreeUtil.getChildOfType(jsExpressionStatement, JSCallExpression.class);
			if (jsCallExpression != null) {
				JSArgumentList argumentList = jsCallExpression.getArgumentList();
				final JSObjectLiteralExpression argumentJSObjectLiteralExpression = PsiTreeUtil.getChildOfType(argumentList, JSObjectLiteralExpression.class);
				if(argumentJSObjectLiteralExpression!=null){
					JSProperty[] properties = argumentJSObjectLiteralExpression.getProperties();
					for (JSProperty argumentJSProperty : properties) {
						String text = "this." + argumentJSProperty.getName() + "=" + argumentJSProperty.getValue().getText() + ";";
						JSExpressionStatement statement = JSChangeUtil.createStatementPsiFromTextWithContext(text, jsExpressionStatement, JSExpressionStatement.class);
						PsiElement parent = PsiTreeUtil.getStubOrPsiParent(jsExpressionStatement);
						parent.addBefore(statement, jsExpressionStatement);
					}
					jsExpressionStatement.delete();
				}
			}
		}
	}

	public static Pair<Boolean, JSObjectLiteralExpression> getJSFileInfo(PsiFile psiFile) {
		final Boolean[] propertyTypeEnum = {false};
		final JSObjectLiteralExpression[] literalExpression = {null};
		psiFile.acceptChildren(new PsiRecursiveElementVisitor() {
			@Override
			public void visitElement(@NotNull PsiElement element) {
				super.visitElement(element);
				if (element instanceof JSExpressionStatement && element.getText().startsWith("Component")) {
					propertyTypeEnum[0] = true;
					JSExpressionStatement jsExpressionStatement = (JSExpressionStatement) element;
					JSExpression expression = jsExpressionStatement.getExpression();
					if (expression instanceof JSCallExpression) {
						JSCallExpression jsCallExpression = (JSCallExpression) expression;
						JSArgumentList argumentList = jsCallExpression.getArgumentList();
						literalExpression[0] = PsiTreeUtil.getChildOfType(argumentList, JSObjectLiteralExpression.class);
					}
				}
			}
		});
		return new Pair<>(propertyTypeEnum[0], literalExpression[0]);
	}
}
